/// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VSPowerToys.Common.TaskManager.ContentInstaller;
using Microsoft.VSPowerToys.Common.TaskManager.ZipLibrary;
using System.Collections.ObjectModel;
using System.IO;

namespace Microsoft.VSPowerToys.Common.TaskManager
{
    /// <summary>
    /// A task for installing VSI content.
    /// </summary>
    public class InstallVsiTask : Task
    {

        private bool compressed;

        private string location;

        /// <summary>
        /// Flag for cancelling the task
        /// </summary>
        private bool cancelTask;

        /// <summary>
        /// Creates a new VSI installation task
        /// </summary>
        /// <param name="compressed">true if task is going to use a VSI file, false if using vscontent file</param>
        /// <param name="location">Path to vscontent file or VSI file</param>
        public InstallVsiTask(string location, bool compressed) : 
            base(location)
        {
            this.compressed = compressed;
            this.location = location;
        }
        
        public override bool IsCancellable
        {
            get { return true; }
        }

        /// <summary>
        /// In order to install VSI content, first VSI file is extracted to a temporary location
        /// and all vscontent files are processed to check for contents to install.
        /// 
        /// Then each content is installed individually.
        /// </summary>
        /// <remarks>
        /// If a content couldn't be installed, it is skipped and the installation continues.
        /// </remarks>
        public override void PerformTask()
        {
            Collection<string> contentFiles;
            bool createdTempPath = false;
            if (this.compressed)
            {
                this.UncompressFile();
                createdTempPath = true;
                //// Find all content files in the temporary location
                contentFiles = FindContentFile(this.location);
            }
            else
            {
                contentFiles = new Collection<string>();
                if (File.Exists(this.location))
                {
                    contentFiles.Add(this.location);
                }
            }
            
            //// Process all content files found in target location
            foreach (string contentFile in contentFiles)
            {
                VsiPackage package = new VsiPackage();
                if (package.ReadVSContentFile(contentFile))
                {
                    if (package.Contents.Count == 0)
                    {
                        this.PostMessage(String.Format(System.Globalization.CultureInfo.CurrentCulture,
                            ExceptionStrings.NoValidContent, Path.GetFileName(contentFile)));
                    }
                    foreach (BaseVSContent content in package.Contents)
                    {
                        if (this.cancelTask)
                        {
                            throw new TaskException(ExceptionStrings.CancelledString, this);
                        }
                        try
                        {
                            content.InstallContent();
                        }
                        catch (VsiException ex)
                        {
                            this.PostMessage(String.Format(System.Globalization.CultureInfo.CurrentCulture,
                                ExceptionStrings.ContentInstallFailed,
                                content.DisplayName, ex.Message));
                        }
                    }
                }
                else
                {
                    throw new TaskException(String.Format(System.Globalization.CultureInfo.CurrentCulture,
                                ExceptionStrings.InvalidVSIFile,
                                Path.GetFileName(contentFile)), this);
                }
            }

            /// Delete the temp directory containing uncompressed files
            if (createdTempPath)
            {
                try
                {
                    Directory.Delete(this.location, true);
                }
                catch (IOException)
                {
                }
            }
        }

        /// <summary>
        /// Cancels the task after the current content is installed by setting the cancel flag.
        /// </summary>
        protected override void CancelTaskInternal()
        {
            this.cancelTask = true;
        }

        /// <summary>
        /// Uncompresses the VSI file in to a temporary location
        /// </summary>
        private void UncompressFile()
        {
            if (!compressed) return;
            ZipFile zipFile = ZipFile.Read(this.location);
            if (zipFile == null)
            {
                throw new TaskException(String.Format(System.Globalization.CultureInfo.CurrentCulture,
                    ExceptionStrings.InvalidVSIFile,
                    Path.GetFileName(this.location)));
            }
            string tempPath = Path.GetTempFileName();
            File.Delete(tempPath);
            Directory.CreateDirectory(tempPath);
            zipFile.ExtractAll(tempPath);
            this.location = tempPath;
            this.compressed = false;
        }

        /// <summary>
        /// Finds all vscontent files in the given path.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static Collection<string> FindContentFile(string path)
        {
            Collection<string> files = new Collection<string>();
            if (Directory.Exists(path))
            {
                DirectoryInfo dir = new DirectoryInfo(path);
                foreach (FileInfo file in dir.GetFiles("*.vscontent", SearchOption.TopDirectoryOnly)) 
                {
                    files.Add(file.FullName);
                }
            }
            return files;
        }
    }
}

